home *** CD-ROM | disk | FTP | other *** search
- /*
- File: CMValOps.c
-
- Contains: Container Manager Value Operations
-
- Written by: Ira L. Ruben
-
- Owned by: Ed Lai
-
- Copyright: © 1991 - 1996 by Apple Computer, Inc., all rights reserved.
-
- Change History (most recent first):
-
- <4> 8/19/96 DH 1376276:OpenDoc corrupts document when
- running out of disk space. Added function
- to get useCount for values.
- <3> 8/13/96 DM 1362809: disable containers on error
- <2> 1/15/96 TJ Cleaned Up
- <4> 12/9/94 EL #1182275 Optionally do not maintain
- continue flag.
- <3> 9/30/94 EL #1182275 Cut down processing when size is 0
- in write data.
- <2> 8/26/94 EL #1182275 Cut down unnecessary read before
- write.
- <3> 6/3/94 EL Rename Values.h/c to ValueRtn.h/c.
- <2> 3/17/94 EL update to zero length value will not show
- up. For small value compare with original
- value to avoid unnecessary write. #1147893
- <1> 2/3/94 EL first checked in
- <6> 2/1/94 EL Check for null target container to make
- memory use safe for multitasking.
- <5> 1/6/94 EL Fix bug that freelist is not used if the
- old value has no data.
- <4> 10/6/93 EL Rename CMGetAdjacentXXXX to GetAdjacentXXXX
- and static near.
- <3> 10/4/93 EL Let CMGetPrevValue and CMGetNextValue share
- CMGetAdjacentValue.
- <2> 9/27/93 VL Added CMGetPrevValue.
-
- To Do:
- */
-
- /*---------------------------------------------------------------------------*
- | |
- | <<< CMValOps.c >>> |
- | |
- | Container Manager Value Operations |
- | |
- | Ira L. Ruben |
- | 12/15/91 |
- | |
- | Copyright Apple Computer, Inc. 1991-1994 |
- | All rights reserved. |
- | |
- *---------------------------------------------------------------------------*
-
- An object's property is given a typed value by writing to that value. Conversely, you
- get the value for an object's property by reading the value. Before you do either of
- these operations you must get a refNum for a value. So depending in input or output
- there are four key value operations.
-
- 1. CMUseValue() returns a refNum to an existing value.
- 2. CMNewValue() creates and returns a refNum for a new value.
- 3. CMReadValueData() reads the value
- 4. CMWriteValueData() writes (sets) the value
-
- These routines are all defined in this file. Other value routines are provided for doing
- various manipulations and getting or setting certain information about the values. Of
- all the API routines defined in the Container Manager, you may view the CMReadValueData()
- and CMWriteValueData() as two of the most important! As it turns out they are two of the
- most complicated routines in the Container Manager. CMWriteValueData() is one of the
- worst! (just thought I would worn you).
- */
-
-
- #include <stddef.h>
- #include <stdio.h>
- #include <stdarg.h>
-
- #ifndef __CMTYPES__
- #include "CMTypes.h"
- #endif
- #ifndef __CM_API__
- #include "CMAPI.h"
- #endif
- #ifndef __LISTMGR__
- #include "ListMgr.h"
- #endif
- #ifndef __DYNAMICVALUES__
- #include "DynValus.h"
- #endif
- #ifndef __VALUEROUTINES__
- #include "ValueRtn.h"
- #endif
- #ifndef __TOCENTRIES__
- #include "TOCEnts.h"
- #endif
- #ifndef __TOCOBJECTS__
- #include "TOCObjs.h"
- #endif
- #ifndef __GLOBALNAMES__
- #include "GlbNames.h"
- #endif
- #ifndef __CONTAINEROPS__
- #include "Containr.h"
- #endif
- #ifndef __HANDLERS__
- #include "Handlers.h"
- #endif
- #ifndef __UPDATING__
- #include "Update.h"
- #endif
- #ifndef __FREESPACE__
- #include "FreeSpce.h"
- #endif
- #ifndef __SESSIONDATA__
- #include "Session.h"
- #endif
- #ifndef __ERRORRPT__
- #include "ErrorRpt.h"
- #endif
- #ifndef __UTILITYROUTINES__
- #include "Utility.h"
- #endif
-
- CM_CFUNCTIONS
-
- /* The following generates a segment directive for Mac only due to 32K Jump Table */
- /* Limitations. If you don't know what I'm talking about don't worry about it. The */
- /* default is not to generate the pragma. Theoritically unknown pragmas in ANSI won't */
- /* choke compilers that don't recognize them. */
-
- #if CM_MPW
- #pragma segment CMValueOps
- #endif
-
- /*---------------------------------------------------------------------*
- | CMCountValues - count the number of values for an object's property |
- *---------------------------------------------------------------------*
-
- A property for an object can be defined to have any number of typed values. The types
- for all the values for a given object property are unique. This routine is used to
- determine the total number of values for a object's property or whether a value for a
- given type is present for that object's property.
-
- If the type is specified as NULL, the total number of values for the object's property
- is returned. If the type is not NULL, 1 is returned if a value of that type is present
- (because there can be a maximum of one value of that type), and 0 otherwise. If the
- property is not defined for the object, 0 is also returned.
- */
-
- CMCount CM_FIXEDARGS CMCountValues(CMObject object, CMProperty property, CMType type)
- {
- TOCPropertyPtr theProperty;
- TOCValueHdrPtr theValueHdr;
- ContainerPtr container;
- ContainerPtr propCtr;
-
- ExitIfBadObject(object, 0); /* validate object */
- ExitIfBadProperty(property, 0); /* validate property */
-
- container = ((TOCObjectPtr)object)->container;
-
- /* Process explicitly specified type separately... */
-
- propCtr = ((TOCObjectPtr)property)->container;
-
- if (type != NULL) {
- ExitIfBadType(type, 0); /* validate type */
-
- { ContainerPtr typeCtr = ((TOCObjectPtr)type)->container;
- if (container->targetContainer != propCtr->targetContainer ||
- container->targetContainer != typeCtr->targetContainer) {
- Container_Disable(container);
- Container_Disable(propCtr);
- Container_Disable(typeCtr);
- ERROR3(CM_err_3Containers, CONTAINERNAMEx(container),
- CONTAINERNAMEx(propCtr),
- CONTAINERNAMEx(typeCtr));
- return (0);
- }
- }
-
- /* Find the TOCProperty belonging to the object with the property ID of the */
- /* specified property object... */
-
- theProperty = cmGetObjectProperty((TOCObjectPtr)object,
- ((TOCObjectPtr)property)->objectID);
- if (theProperty == NULL) return (0);
-
- /* Find the value for the object's property that has the desired type... */
-
- theValueHdr = cmGetPropertyType(theProperty, ((TOCObjectPtr)type)->objectID);
-
- return ((CMCount)(theValueHdr != NULL));
- }
-
- /* From here on we're interested in just the total number of values... */
-
- if (container->targetContainer != propCtr->targetContainer) {
- Container_Disable(container);
- Container_Disable(propCtr);
- ERROR2(CM_err_2Containers, CONTAINERNAMEx(container),
- CONTAINERNAMEx(propCtr));
- return (0);
- }
-
- /* Find the TOCProperty belonging to the object with the property ID of the specified */
- /* property object... */
-
- theProperty = cmGetObjectProperty((TOCObjectPtr)object,
- ((TOCObjectPtr)property)->objectID);
-
- return (theProperty ? (CMCount)cmCountListCells(&theProperty->valueHdrList) : 0);
- }
-
-
- /*----------------------------------------------------------*
- | CMUseValue - get a refNum for an existing value (header) |
- *----------------------------------------------------------*
-
- This routine is used to get the refNum for the value of an object's property of the
- given type. NULL is returned if the value does not exist, or if or the object does not
- contain the property. If the type of the value corresponds to a global type name that has
- an associated "use value" handler, or if it's base types (if any) have associated "use
- value" handlers, a dynamic value will be created and returned.
-
- Note, if the value is used as an embedded container, then that embedded container must be
- opened and read using CMOpenContainer(). The data, i.e, the embedded container for such a
- value can only be defined by using CMOpenNewContainer(). The container type name must be
- associated with a special set of handlers that define a "return parent value" handler.
- This handler returns the parent value refNum whose data contains the embedded container.
-
- There is no restriction on reading the data for an embedded container like any other value
- data using CMReadValueData(). However, the data for an embedded container value includes a
- TOC. Unless a "blind" copy is being done, the TOC read this way is of not very much use!
- */
-
- CMValue CM_FIXEDARGS CMUseValue(CMObject object, CMProperty property, CMType type)
- {
- TOCPropertyPtr theProperty;
- TOCValueHdrPtr theValueHdr, baseValueHdr;
- ContainerPtr container;
-
- ExitIfBadObject(object, NULL); /* validate object */
- ExitIfBadProperty(property, NULL); /* validate property */
- ExitIfBadType(type, NULL); /* validate type */
-
- container = ((TOCObjectPtr)object)->container;
-
- { ContainerPtr propCtr = ((TOCObjectPtr)property)->container;
- ContainerPtr typeCtr = ((TOCObjectPtr)type)->container;
-
- if (container->targetContainer != propCtr->targetContainer ||
- container->targetContainer != typeCtr->targetContainer) {
- Container_Disable(container);
- Container_Disable(propCtr);
- Container_Disable(typeCtr);
- ERROR3(CM_err_3Containers, CONTAINERNAMEx(container),
- CONTAINERNAMEx(propCtr),
- CONTAINERNAMEx(typeCtr));
- return (NULL);
- }
- }
-
- /* Find the TOCProperty belonging to the object with the property ID of the specified */
- /* property object (got that?)... */
-
- theProperty = cmGetObjectProperty((TOCObjectPtr)object,
- ((TOCObjectPtr)property)->objectID);
- if (theProperty == NULL) /* if property not in object... */
- return (NULL); /* ..tell user about it */
-
- /* If the resulting value is a dynamic value, just return it. If it isn't, this might */
- /* be the first use of the value. In that case we must check to see if a dynamic */
- /* value must be created. This is done by calling cmHasUseValueHandler() which checks */
- /* to see if the value's type has a registered metahandler, and that the metahandler */
- /* defines a "use value" handler. If that is the case, we call cmFollowTypes() to */
- /* do a depth-first processing of all the type's base types to see if layers of */
- /* dynamic values must be created. No matter what, if a dynamic value is created, we */
- /* return that as the result. Otherwise, the original "real" value is returned. */
-
- /* Check to see if a dynamic value must be created. This is done by calling */
- /* cmFollowBaseTypes() to do a depth-first search starting from the given type on all */
- /* of that type's base types. Dynamic values are created for each type that has a */
- /* "use value" handler ("new value" handlers are only required for CMNewValue()). The */
- /* resulting dynamic value is returned or the "real" value if there are no dynamic */
- /* values. */
-
- theValueHdr = (TOCValueHdrPtr)cmGetPropertyType(theProperty, ((TOCObjectPtr)type)->objectID);
-
- if (theValueHdr) { /* if we have a value header... */
- if (theValueHdr->dynValueData.dynValue != NULL){/* ...if dynamic value exists... */
- theValueHdr = theValueHdr->dynValueData.dynValue;/*...just use it, but... */
- ++theValueHdr->useCount; /* ...count dynamic value uses */
- } else { /* ...if no dynamic value yet... */
- baseValueHdr = theValueHdr;
- theValueHdr = cmFollowTypes(theValueHdr, (TOCObjectPtr)type, false, NULL);
- ++baseValueHdr->useCount; /* incr use count on base value */
- }
- }
-
- return ((CMValue)theValueHdr); /* return real or dynamic value */
- }
-
-
- /*--------------------------------------------------------------------------------*
- | getAdjacentValue - use the adjacent value for a specified proprty of an object |
- *--------------------------------------------------------------------------------*
-
- This routine returns the refNum for the next/prev type value (according to the current value
- order) in the objects property following currValue. If currValue is NULL, the refNum for
- the first/last value for that object's property is returned. If currValue is not NULL, the
- next/prev value for that object's property is returned. NULL is returned if there are no more
- type values following/preceding currValue or the object does not contain the property.
-
- currValue is generally a refNum previously returned from this routine. Successive calls
- to this routine will thus yield all the values for the specified property of the specified
- object as long as no other operations change the value order.
-
- Note, this routine implicitly does a CMUseValue() on the returned refNum. Thus a dynamic
- value might be spawned. A CMReleaseValue() is therfore required to reduce the use count
- of the refNum.
- */
-
- static CMValue CM_NEAR getAdjacentValue(CMObject object, CMProperty property, CMValue currValue,
- CMBoolean forward)
- {
- TOCPropertyPtr theProperty;
- TOCValueHdrPtr theValueHdr;
- ContainerPtr container;
- CMType type;
-
- ExitIfBadObject(object, NULL); /* validate object */
- ExitIfBadProperty(property, NULL); /* validate property */
-
- container = ((TOCObjectPtr)object)->container;
-
- { ContainerPtr propCtr = ((TOCObjectPtr)property)->container;
- if (container->targetContainer != propCtr->targetContainer) {
- Container_Disable(container);
- Container_Disable(propCtr);
- ERROR2(CM_err_2Containers, CONTAINERNAMEx(container),
- CONTAINERNAMEx(propCtr));
- return (NULL);
- }
- }
-
- /* Find the TOCProperty belonging to the object with the property ID of the specified */
- /* property object... */
-
- theProperty = cmGetObjectProperty((TOCObjectPtr)object,
- ((TOCObjectPtr)property)->objectID);
- if (theProperty == NULL) /* if property not in object... */
- return (NULL); /* ..tell user about it */
-
- /* For the first time, return the first/last value on the property's list... */
-
- if (forward)
- if (currValue == NULL) /* NULL ==> use head of list */
- theValueHdr = (TOCValueHdrPtr)cmGetListHead(&theProperty->valueHdrList);
- else { /* use next value in sequence */
- ExitIfBadValue(currValue, NULL); /* validate currValue */
- theValueHdr = (TOCValueHdrPtr)cmGetNextListCell(currValue);
- }
- else
- if (currValue == NULL) /* NULL ==> use tail of list */
- theValueHdr = (TOCValueHdrPtr)cmGetListTail(&theProperty->valueHdrList);
- else { /* use previous value in sequence */
- ExitIfBadValue(currValue, NULL); /* validate currValue */
- theValueHdr = (TOCValueHdrPtr)cmGetPrevListCell(currValue);
- }
-
- /* We now have the value the caller wants. But calling this routine is the "moral */
- /* equivalent" of a CMUseValue() since we are returning a value refNum. This must be */
- /* like a CMUseValue() because the user will probably want to do a CMReleaseValue() */
- /* and also potentially spawn a dynamic value. At the very lease the CMReleaseValue() */
- /* will want the use counts to be correct. So what we must do here is do the */
- /* CMUseValue() for the user. To do that we need the type object refNum. So... */
-
- if (theValueHdr != NULL) { /* just to be safe! */
- type = (CMType)cmFindObject(container->toc, theValueHdr->typeID); /* type refNum */
- if (type == NULL) /* if should have been found! */
- theValueHdr = NULL; /* ...return NULL if it wasn't */
- else /* do the CMUseValue() for the user */
- theValueHdr = (TOCValueHdrPtr)CMUseValue(object, property, type);
- }
-
- return ((CMValue)theValueHdr); /* NULL or CMUseValue() result */
- }
-
-
- /*--------------------------------------------------------------------------*
- | CMGetNextValue - use the next value for a specified proprty of an object |
- *--------------------------------------------------------------------------*
-
- Just call getAdjacentValue with forward.
- */
-
- CMValue CM_FIXEDARGS CMGetNextValue(CMObject object, CMProperty property, CMValue currValue)
- {
- return getAdjacentValue(object, property, currValue, true);
- }
-
- /*------------------------------------------------------------------------------*
- | CMGetPrevValue - use the previous value for a specified proprty of an object |
- *------------------------------------------------------------------------------*
-
- Just call getAdjacentValue with forward = false.
- */
-
- CMValue CM_FIXEDARGS CMGetPrevValue(CMObject object, CMProperty property, CMValue currValue)
- {
- return getAdjacentValue(object, property, currValue, false);
- }
-
-
- /*-------------------------------------------------------*
- | CMNewValue - create a refNum for a new value (header) |
- *-------------------------------------------------------*
-
- A new value entry is created for the specified object with the specified property and type
- and its refNum returned. If the specified type corresponds to a global type name that has
- an associated "use value" handler, or if its base types (if any) have associated "use
- value" handlers, a dynamic value will be created and returned.
-
- An object's properties can have more than one value. However, the all the types for the
- values belonging to a given object property must be UNIQUE. It is an error to attempt to
- create a value for a property when there is already a value of the same type for that
- property.
-
- Note, the refNum returned from here represents a yet-to-be-set value. The value data is
- set with CMWriteValueData(), or if it's for an embedded container, CMOpenNewContainer().
- For creating embedded containers, the container type name must be associated with a set
- of container handlers that support the "return parent value" handler. Such a handler
- returns the parent value refNum whose data will contain the embedded container. No value
- value data must exist for the value when the container is opened. This value gets its
- data by using it as an embedded container instead of doing CMWriteValueData()'s to it.
-
- Note also, the value is created at an unspecified location in the sequence of values for
- the specified property. The order of the values may change.
-
- As mentioned above, if the type, or any of its base types cause dynamic values to be
- created, the process of creating dynamic values may required data initialization
- parameters for the "new value" handler (see DynValus.c for a complete description of
- dynamic values and the "new value" handler). These are suppled from the CMNewValue()
- "..." parameters. They are "decoded" using a metadata format description returned from
- a "metadata" handler.
-
- The ORDER of the parameters is important! Because base types are done with a depth-first
- search through the types down to their leaves, the CMNewValue() "..." parameters MUST be
- ordered for the "deepest" type first. For example, given the following type inheritance
- heirarchy (read these as T1 inherits from T2 and T3, and so on):
-
- T4 T5
- * *
- * *
- T2 T3
- * *
- * *
- T1
-
- The depth-first search, starting at T1, yields the sequence: T2 T4 T5 T3 T1. Then this
- is the order the CMNewValue() "..." parameters must be in.
-
- Implementation note: the "guts" of this routine is in CMVNewValue(). We just do what a
- user would do to use that routine.
- */
-
- CMValue CM_VARARGS CMNewValue(CMObject object, CMProperty property, CMType type, ...)
- {
- CMValue value;
- va_list dataInitParams;
-
- va_start(dataInitParams, type); /* get ptr to the "..." parameters */
- value = CMVNewValue(object, property, type, dataInitParams);
- va_end(dataInitParams);
-
- return (value);
- }
-
-
- /*--------------------------------------------------------*
- | CMVNewValue - create a refNum for a new value (header) |
- *--------------------------------------------------------*
-
- This routine is the same as CMNewValue() above, except that the dynamic value data
- initialization (i.e., "...") parameters are given as a variable argument list as defined
- by the "stdarg" facility.
-
- This routine assumes the caller sets up and terminates the variable arg list using the
- "stdarg.h" calls as follows:
-
- #include <stdarg.h>
-
- callersRoutine(args, ...)
- {
- va_list dataInitParams;
-
- - - -
-
- va_start(dataInitParams, args);
- value = CMVNewValue(object, property, type, dataInitParams);
- va_end(dataInitParams);
-
- - - -
- }
-
- See CMNewValue() for further details.
- */
-
- CMValue CM_FIXEDARGS CMVNewValue(CMObject object, CMProperty property, CMType type,
- va_list dataInitParams)
- {
- TOCObjectPtr theObject;
- TOCValueHdrPtr theValueHdr, baseValueHdr;
- ContainerPtr container;
-
- ExitIfBadObject(object, NULL); /* validate object */
- ExitIfBadProperty(property, NULL); /* validate property */
- ExitIfBadType(type, NULL); /* validate type */
-
- container = ((TOCObjectPtr)object)->container;
-
- { ContainerPtr propCtr = ((TOCObjectPtr)property)->container;
- ContainerPtr typeCtr = ((TOCObjectPtr)type)->container;
-
- if (container->targetContainer != propCtr->targetContainer ||
- container->targetContainer != typeCtr->targetContainer) {
- Container_Disable(container);
- Container_Disable(propCtr);
- Container_Disable(typeCtr);
- ERROR3(CM_err_3Containers, CONTAINERNAMEx(container),
- CONTAINERNAMEx(propCtr),
- CONTAINERNAMEx(typeCtr));
- return (NULL);
- }
- }
-
- container = container->updatingContainer; /* use updating container from here on*/
-
- /* This will create a value header for an existing object or a new object with a */
- /* value header but no value. Other "CM..." calls will create the actual value(s). */
-
- theObject = cmDefineObject(container, ((TOCObjectPtr)object)->objectID,
- ((TOCObjectPtr)property)->objectID,
- ((TOCObjectPtr)type)->objectID,
- NULL, container->generation, 0,
- ObjectObject, &theValueHdr);
- if (theObject == NULL) return (NULL);
-
- /* Check to see if a dynamic value must be created. This is done by calling */
- /* cmFollowBaseTypes() to do a depth-first search starting from the given type on all */
- /* of that type's base types. Dynamic values are created for each type that has a */
- /* "use value" and "new value" handler. The resulting dynamic value is returned or */
- /* the "real" value we created above if there are no dynamic values. */
-
- baseValueHdr = theValueHdr;
- theValueHdr = cmFollowTypes(theValueHdr, (TOCObjectPtr)type, true, &dataInitParams);
-
- baseValueHdr->useCount = 1; /* set use count of "real" value */
-
- return ((CMValue)theValueHdr); /* return real or dynamic value */
- }
-
-
- /*--------------------------------------------------------*
- | CMGetBaseValue - get the base value of a dynamic value |
- *--------------------------------------------------------*
-
- Returns the base value for a dynamic value and NULL if the value is not a dynamic value.
- It is expected that this routine will only be called from dynamic value handlers that
- support layered handlers. Indeed, this is enforced!
- */
-
- CMValue CM_FIXEDARGS CMGetBaseValue(CMValue value)
- {
- TOCValueHdrPtr theValueHdr;
- ContainerPtr container;
-
- ExitIfBadValue(value, NULL); /* validate value */
-
- theValueHdr = (TOCValueHdrPtr)value;
- container = theValueHdr->container;
-
- if (container->getBaseValueOk <= 0) { /* only callable from a handler */
- Container_Disable(container);
- ERROR1(CM_err_CantGetBase, CONTAINERNAME);
- return (NULL);
- }
-
- if (!IsDynamicValue(value)) /* if not dynamic value... */
- return (NULL); /* ...there is no base */
-
- return ((CMValue)DYNEXTENSIONS(value)->baseValue);/* return the base value */
- }
-
-
- /*--------------------------------------------*
- | CMGetValueSize - get the size of the value |
- *--------------------------------------------*
-
- The character size of the value data is returned. The size returned is 0 if the value
- has no data.
-
- Note, the size returned is the size of the data as created by CMWriteValueData() (or
- CMOpenNewContainer() for embedded container values, but that's not applicable to this
- discussion here). If this data represents represents some sort of compression scheme,
- the API has NO KNOWLEDGE of the actual size represented by that compressed data.
- */
-
- CMSize CM_FIXEDARGS CMGetValueSize(CMValue value)
- {
- CM_ULONG size = 0;
-
- ExitIfBadValue(value, 0); /* validate value */
-
- if (IsDynamicValue(value)) { /* process dynamic value... */
- GetDynHandlerAddress(value, cmGetValueSize, CMGetValueSizeOpType, "CMGetValueSize", 0);
- if (IsDynamicValue(value)) {
- SignalDynHandlerInUse(value, cmGetValueSize);
- AllowCMGetBaseValue(((TOCValueHdrPtr)value)->container);
- size = (CM_ULONG)CMDynGetValueSize(value);
- DisAllowCMGetBaseValue(((TOCValueHdrPtr)value)->container);
- SignalDynHandlerAvailable(value, cmGetValueSize);
- return (size);
- }
- }
-
- return ((CMSize)((TOCValueHdrPtr)value)->size); /* return size from the value hdr */
- }
-
- /*---------------------------------------------*
- | CMGetValueUseCount - get refcount |
- *---------------------------------------------*
-
- */
-
- CMCount CM_FIXEDARGS CMGetValueUseCount(CMValue value);
-
- CMCount CM_FIXEDARGS CMGetValueUseCount(CMValue value)
- {
- ExitIfBadValue(value, 0); /* validate value */
-
- return ((CMCount)((TOCValueHdrPtr)value)->useCount);
- }
-
- /*---------------------------------------------*
- | CMReadValueData - read the data for a value |
- *---------------------------------------------*
-
- The data, starting at the offset, for the value is read into the buffer. The size of the
- data read is returned. Up to maxSize characters will be read (can be 0).
-
- The data is read starting at the offset, up to the end of the data, or maxSize characters,
- whichever comes first. Offsets are relative to 0. If the starting offset is greater than
- or equal to the current data size, no data is read and 0 returned.
-
- Normally CMReadValueData() is used on a container that was opened for input using
- CMOpenContainer(). However, it is permitted to read data already written to a container
- that has been opened with CMOpenNewContainer().
-
- It is an error to attempt to read a value which has no data, i.e., a value where only a
- CMNewValue() has been done.
- */
-
- CMSize CM_FIXEDARGS CMReadValueData(CMValue value, CMPtr buffer, CMCount offset, CMSize maxSize)
- {
- TOCValueHdrPtr theValueHdr;
- TOCValuePtr theValue;
- ContainerPtr container;
- CM_UCHAR *p;
- CM_ULONG len, remaining, totalRead, amountRead;
-
- ExitIfBadValue(value, 0); /* validate value */
-
- container = ((TOCValueHdrPtr)value)->container; /* NEVER use updatingContainer here!*/
-
- if (IsDynamicValue(value)) { /* process dynamic value... */
- GetDynHandlerAddress(value, cmReadValueData, CMReadValueDataOpType, "CMReadValueData", 0);
- if (IsDynamicValue(value)) {
- SignalDynHandlerInUse(value, cmReadValueData);
- AllowCMGetBaseValue(container);
- totalRead = (CM_ULONG)CMDynReadValueData(value, buffer, offset, maxSize);
- DisAllowCMGetBaseValue(container);
- SignalDynHandlerAvailable(value, cmReadValueData);
- return (totalRead);
- }
- }
-
- theValueHdr = (TOCValueHdrPtr)value; /* ("this") value may have changed! */
-
- #if 0 /* removed check to allow reading stuff written to an output container */
- if (container->useFlags & kCMWriting) { /* make sure we're opend for reading*/
- Container_Disable(container);
- ERROR1(CM_err_ReadIllegal, CONTAINERNAME);
- return (0);
- }
- #endif
-
- if (maxSize == 0 || buffer == NULL) return (0);
-
- /* Scan across the value list for the value header and concat data into buffer. A */
- /* value list will exist for continued values. The user views the data as one */
- /* contiguous chunk of stuff. But a continued value is acutally a set of non- */
- /* contiguous smaller chunks that we concat into the user's buffer. The user specifies*/
- /* a starting offset with respect to his or her view of the contiguous data. We must */
- /* map that offset into an offest within the proper starting continued value segment. */
- /* Fortunately (yea, right) we have cmGetStartingValue() to save the day! It does */
- /* the mapping (funny thing about that, isn't it?). */
-
- if (cmIsEmptyList(&theValueHdr->valueList)) { /* must have a value */
- Container_Disable(container);
- ERROR1(CM_err_HasNoValue, CONTAINERNAME);
- return (0);
- }
-
- theValue = cmGetStartingValue(theValueHdr, offset, &offset); /* get start seg/offset */
- if (theValue == NULL) return (0); /* offset out of range */
-
- p = (CM_UCHAR *)buffer; /* p points to next byte to read to */
- remaining = (CM_ULONG)maxSize; /* remaining bytes to read */
- totalRead = 0; /* how much we actually do read */
-
- while (theValue && remaining) { /* read value (segments)... */
- len = cmGet1ValueSize(theValue) - offset; /* (note, immediates work here) */
- if (len > remaining) len = remaining; /* (note, so do global names) */
-
- if (len) { /* if more is wanted... */
- amountRead = cmRead1ValueData(theValue, p, offset, len); /* read 1 value segment */
- totalRead += amountRead; /* keep track of how much we read */
- if (amountRead < len) break; /* if end of total value, we're done*/
- p += len; /* point at next free byte in buffer*/
- }
-
- offset = 0; /* full segments from now on */
- remaining -= len; /* adjust what's remaining to read*/
- theValue = (TOCValuePtr)cmGetNextListCell(theValue); /* point to next value seg */
- }
-
- return (totalRead); /* return total amount concatenated */
- }
-
-
- /*------------------------------------------*
- | CMWriteValueData - write data to a value |
- *------------------------------------------*
-
- The buffer is written to the container and defined as the data for the value. If the
- value already has data associated with it, the buffer overwrites the "old" data starting
- at the offset character position. size bytes are written. size can be 0.
-
- If the current size of the value data is T (it will be 0 for a new value created by
- CMNewValue()), then the offset may be any value from 0 to T. That is, existing data
- may be overwritten or the value extended with new data. The value of T can be gotton
- using CMGetValueSize(). Note, no "holes" can be created. An offset cannot be greater
- than T.
-
- Once data has been written to the container, it may be read using CMReadValueData(). Note,
- that CMReadValueData() is also used for containers opened with for input using
- CMOpenContainer(). It thus can be used for all kinds of opens. The converse is not true.
- CMWriteValueData() may only be used for a container opend for writing (or converting)
- using CMOpenNewContainer().
-
- CMWriteValueData() calls for a particular value do not have to be contiguous. Writes
- for other values can be done. The API, specifically, this routine here, takes care of
- generating "continued" value data (segments) for a value. The data is physically not
- contiguous in the container with such a case. CMWriteValueData() hides this by allowing
- the user to view the data as contiguous. The input offset is mapped into the proper
- starting segment and offset within that.
-
- It is an error to write to protected values. This includes the predefined TOC objects
- (seed and offset values) and objects representing currently opened embedded containers.
-
- For creating embedded containers, CMOPenNewContainer() is used instead of
- CMWriteValueData(). See CMNewValue() and CMOpenContainer() for further details.
- */
-
- void CM_FIXEDARGS CMWriteValueData(CMValue value, CMPtr buffer, CMCount offset, CMSize size)
- {
- TOCValueHdrPtr theValueHdr;
- TOCValuePtr theValue, theNextValue;
- ContainerPtr container;
- CM_UCHAR *p;
- CM_CHAR offsetStr[15];
- CM_ULONG len, remaining, amountWritten, nextFree, valueSize;
- TOCValueBytes valueBytes;
- #if SizeReadBeforeWrite
- CM_CHAR tempBuf[SizeReadBeforeWrite];
- #endif
-
- ExitIfBadValue(value, CM_NOVALUE); /* validate value */
- if (buffer == NULL) return;
-
- theValueHdr = (TOCValueHdrPtr)value;
- container = theValueHdr->container->updatingContainer;
-
- if ((container->useFlags & kCMWriting) == 0) { /* make sure opened for writing */
- Container_Disable(container);
- ERROR1(CM_err_WriteIllegal1, CONTAINERNAME);
- return;
- }
-
- if ((theValueHdr->valueFlags & ValueProtected) != 0){ /* can't write if protected! */
- Container_Disable(container);
- ERROR1(CM_err_WriteIllegal2, CONTAINERNAME);
- return;
- }
-
- if ((theValueHdr->valueFlags & ValueGlobal) != 0) { /* can't write to global names */
- Container_Disable(container);
- ERROR1(CM_err_CantWriteGlbl, CONTAINERNAME);
- return;
- }
-
- if (IsDynamicValue(value)) { /* process dynamic value... */
- GetDynHandlerAddress(value, cmWriteValueData, CMWriteValueDataOpType, "CMWriteValueData", CM_NOVALUE);
- if (IsDynamicValue(value)) {
- SignalDynHandlerInUse(value, cmWriteValueData);
- AllowCMGetBaseValue(container);
- CMDynWriteValueData(value, buffer, offset, size);
- DisAllowCMGetBaseValue(container);
- SignalDynHandlerAvailable(value, cmWriteValueData);
- return;
- }
- theValueHdr = (TOCValueHdrPtr)value; /* ("this") value may have changed! */
- }
-
- valueSize = CMGetValueSize((CMValue)theValueHdr); /* get current size of value */
- if (valueSize == 0) /* no data, treat as empty list */
- cmFreeAllListCells(&theValueHdr->valueList, SESSION);
-
- /* If the value list is empty, create the first or only value for this value header. */
- /* An immediate value is created if the value size is less than or equal to the */
- /* sizeof(CM_ULONG). Otherwise we write the data to the container and set the TOC */
- /* info with the offset to it. */
-
- if (cmIsEmptyList(&theValueHdr->valueList)) { /* if we have a empty value list... */
- if (offset > 0) { /* ...create initial value */
- Container_Disable(container);
- ERROR2(CM_err_Offset2Big, cmltostr(offset, 1, false, offsetStr), CONTAINERNAME);
- return;
- }
-
- if (size <= sizeof(CM_ULONG)) { /* we can make value immediate... */
- (void)cmSetValueBytes(container, &valueBytes, Value_Imm_Chars, (CM_ULONG)buffer, size);
- cmAppendValue(theValueHdr, &valueBytes, kCMImmediate);
- cmTouchImmediateValue(theValueHdr); /* touch for updating if necessary */
- } else if (size != 0) { /* value must be written... */
- #if 0
- nextFree = CMgetContainerSize(container);
- CMfseek(container, 0, kCMSeekEnd); /* position to current eof */
- if (CMfwrite(container, buffer, sizeof(CM_UCHAR), size) != size) {
- Container_Disable(container);
- ERROR1(CM_err_BadWrite, CONTAINERNAME);
- return;
- }
- (void)cmSetValueBytes(container, &valueBytes, Value_NotImm, nextFree, size);
- cmAppendValue(theValueHdr, &valueBytes, 0);
- container->physicalEOF = nextFree + size; /* update next free container byte */
- SetLogicalEOF(container->physicalEOF); /* logical EOF == physical EOF */
- #endif
- if ((CMSize)cmWriteData(theValueHdr, (CM_UCHAR *)buffer, (CM_ULONG)size) != size)
- { Container_Disable(container);
- ERROR1(CM_err_BadWrite, CONTAINERNAME);
- }
- cmTouchEditedValue(theValueHdr); /* touch for updating if necessary */
- }
-
- return; /* exit */
- } /* end of 1st value */
-
- if (size == 0) return;
-
- /* At this point we have EXISTING data (possibly already continued). If the offset */
- /* says that we are NOT writing to the end of the data, then we MUST be overwriting */
- /* some of the existing data. In other words, the offset must be less than the total */
- /* size or we have an error. We can only overwrite or append (i.e., concat). We */
- /* cannot create a "hole". In the code that follows we consume enough of the input */
- /* buffer to do the overwriting staring at the offset. If we consume it all, we're */
- /* done. If not, we have reached the end of the existing value and we degenerate into*/
- /* the concat case. */
-
- if (valueSize != offset) { /* here we must be overwriting... */
- if (TouchIt(container, theValueHdr->container)){/* if recording updates... */
- #if SizeReadBeforeWrite
- /* for small write, compare with original value to skip write */
- if (size <= SizeReadBeforeWrite)
- if (offset+size <= theValueHdr->size)
- if (CMReadValueData(value, &tempBuf, offset, size) == size)
- if (memcmp(tempBuf, buffer, size) == 0)
- return; /* no change, don't need to do it */
- #endif
-
- CMDeleteValueData(value, offset, size); /* ...do overwrites this way! */
- CMInsertValueData(value, buffer, offset, size);
- return; /* we're through */
- }
-
- theValue = cmGetStartingValue(theValueHdr, offset, &offset);
- if (theValue == NULL) { /* offset MUST be IN the value */
- Container_Disable(container);
- ERROR2(CM_err_Offset2Big, cmltostr(offset, 1, false, offsetStr), CONTAINERNAME);
- return;
- }
-
- p = (CM_UCHAR *)buffer; /* p points to next byte to write */
- remaining = (CM_ULONG)size; /* remaining bytes to write */
-
- while (theValue && remaining) { /* overwrite existing value data */
- theNextValue = (TOCValuePtr)cmGetNextListCell(theValue); /* get next value seg */
- len = cmGet1ValueSize(theValue) - offset; /* (note, immediates work here!) */
- if (len > remaining) len = remaining;
-
- amountWritten = cmOverwrite1ValueData(theValue, p, offset, len); /* write 1 seg. */
- if (amountWritten == 0) return;
-
- p += amountWritten;
-
- offset = 0; /* full segments from now on */
- remaining -= amountWritten; /* adjust what's remaining to write */
- theValue = theNextValue; /* point to next value seg */
- }
-
- if (remaining == 0) return; /* yikes! all of it was written! */
-
- size = (CMSize)remaining; /* prepare to write rest at end */
- buffer = (CMPtr)p; /* simply fall through to next case */
- } /* end of overwriting existing value data */
-
- /* At this point we want to concat the new data on to the end of the existing data. */
- /* We are doing this because the input offset was equal to the data size or we fell */
- /* through from above because there are still more bytes to write in an overwrite */
- /* and these bytes "stick" off the end of the current data. Again this is a concat */
- /* case. Neat isn't it? */
-
- /* Well, actually no! Unlike the code above here we must handle immediate data */
- /* explicitly. It's sort of a hassle. That's because the amount of new data to */
- /* concat to an immediate might mean that it can't be immediate any more. Immediate */
- /* data is limited in size to less than or equal to sizeof(CM_ULONG). If we can cram */
- /* the new data we do it. If we can't, then we must convert the immediate to a non- */
- /* immediate by writing the data to the container and changing its TOC info to be */
- /* and offset. We then have a non-immediate which falls through to the standard */
- /* concat code for non-immediates. The way all these cases "fall" into one another */
- /* is the "neat" thing here. */
-
- theValue = (TOCValuePtr)cmGetListTail(&theValueHdr->valueList); /* use tail of list */
-
- if (theValue->flags & kCMImmediate) { /* if current value immediate... */
- if (valueSize + size <= sizeof(CM_ULONG)) { /* cram new data if we can... */
- if (size > 0) { /* there must be some data to write */
- memcpy(theValue->value.imm.ucharsValue + valueSize, (CM_CHAR *)buffer, (size_t)size);
- theValue->value.notImm.valueLen = valueSize + size;
- theValueHdr->size += size;
- cmTouchImmediateValue(theValueHdr); /* touch for updating if necessary */
- }
- return; /* that's all we need to do! */
- }
-
- if (!cmConvertImmediate(theValue)) /* convert the immediate... */
- return;
- } /* end of immediate */
-
- /* We are now ready to concat the new data on to the end of the existing data. Here */
- /* too life is not simple. A "concat" means here means a physical concat if the old */
- /* data was the last thing written to the SAME container. If it wasn't we must create */
- /* a new value entry on the valueHdr's value list to represent a continued value. */
- /* Note the emphasis on "SAME" container. We could be writing updates for an "old" */
- /* container to be recorded in a new updating container. */
-
- nextFree = CMgetContainerSize(container);
- if (theValue->value.notImm.value + theValue->value.notImm.valueLen == nextFree &&
- theValue->container == container) { /* remember, must be SAME container!*/
- if (size > 0) { /* there must be some data to write */
- CMfseek(container, 0, kCMSeekEnd); /* make sure of container position */
- if (CMfwrite(container, buffer, sizeof(CM_UCHAR), size) != size) {
- Container_Disable(container);
- ERROR1(CM_err_BadWrite, CONTAINERNAME);
- return;
- }
- container->physicalEOF = nextFree + size; /* update next free container byte */
- SetLogicalEOF(container->physicalEOF); /* logical EOF == physical EOF */
- theValue->value.notImm.valueLen += size; /* update total size */
- theValueHdr->size += size; /* keep size in valueHdr in sync */
- cmTouchEditedValue(theValueHdr); /* touch for updating if necessary */
- }
- return;
- }
-
- /* Too bad! We couldn't really do a concat. We must create a continued value... */
-
- #if 0
- theValue->flags |= kCMContinued; /* flag the current value as cont'd */
- theValueHdr->valueFlags |= ValueContinued; /* also set a more convenient flag */
-
- CMfseek(container, 0, kCMSeekEnd);
- if (CMfwrite(container, buffer, sizeof(CM_UCHAR), size) != size) {
- Container_Disable(container);
- ERROR1(CM_err_BadWrite, CONTAINERNAME);
- return;
- }
- container->physicalEOF = nextFree + size; /* update next free container byte */
- SetLogicalEOF(container->physicalEOF); /* logical EOF == physical EOF */
-
- (void)cmSetValueBytes(container, &valueBytes, Value_NotImm, nextFree, size);
- cmAppendValue(theValueHdr, &valueBytes, 0);
- #endif
-
- if ((CMSize)cmWriteData(theValueHdr, (CM_UCHAR *)buffer, (CM_ULONG)size) != size)
- { Container_Disable(container);
- ERROR1(CM_err_BadWrite, CONTAINERNAME);
- }
- /* If we're recording updates, then define touched list entry for the write... */
-
- if (size > 0) cmTouchEditedValue(theValueHdr); /* touch for updating if necessary */
- }
-
-
- /*------------------------------------------------------*
- | CMDefineValueData - define existing data for a value |
- *------------------------------------------------------*
-
- Existing data in the container is defined as the data for the value. The offset specifies
- a container offset from the beginning of the container. No data is written to the
- container. This call is used only to define values for stuff that is in a non-container
- opened to be converted to container format (kCMConverting useFlags passed to a
- CMOpenNewContainer()). It is an error to use this call in any other context. The offset
- therefore, must be in the range of 0 to the N-1, where N is the size of preexisting data
- at the time the container was opened.
-
- Additional calls to CMDefineValueData() for the SAME value will define additional. i.e.,
- continued, segments when the offset produces noncontiguous data definition. If the size
- of the last (most recent) value segment is T, and the offset for that segment is such
- that offset+T equals the offset for the additional segment, then the last segment is
- simply extended. A new segment is not created. This simulates the exact same sequence
- of events as done by CMWriteValueData().
- */
-
- void CM_FIXEDARGS CMDefineValueData(CMValue value, CMCount offset, CMSize size)
- {
- TOCValueHdrPtr theValueHdr;
- ContainerPtr container;
- TOCValuePtr theValue;
- TOCValueBytes valueBytes;
- char offsetStr[15];
-
- ExitIfBadValue(value, CM_NOVALUE); /* validate value */
-
- theValueHdr = (TOCValueHdrPtr)value;
- container = theValueHdr->container;
-
- if ((container->useFlags & kCMConverting) == 0) { /* must be converting to a container*/
- Container_Disable(container);
- ERROR1(CM_err_NotConverting, CONTAINERNAME);
- return;
- }
-
- #if 0 /* now allow defining of additional continued value segments */
- if (!cmIsEmptyList(&theValueHdr->valueList)) { /* must not have any previous data */
- Container_Disable(container);
- ERROR1(CM_err_HasValue, CONTAINERNAME);
- return;
- }
- #endif
-
- /* Values defined here for a file to be converted to a container must only have */
- /* offsets within the range of the original data. maxValueOffset was the offset to */
- /* the last byte in the original data. We got that when the container was opened. */
-
- if ((CM_ULONG)offset > container->maxValueOffset ||
- (CM_ULONG)offset + size > container->maxValueOffset) {
- Container_Disable(container);
- ERROR2(CM_err_BadDefineData, cmltostr(offset, 1, false, offsetStr), CONTAINERNAME);
- return;
- }
-
- /* If this is the first value definition then no additional work need be done other */
- /* than to create the first value segment. */
-
- if (cmIsEmptyList(&theValueHdr->valueList)) { /* define 1st data segment */
- (void)cmSetValueBytes(container, &valueBytes, Value_NotImm, offset, size);
- cmAppendValue(theValueHdr, &valueBytes, 0);
- return;
- } /* end of 1st value */
-
- /* At this point we want to add an additional data definition to an existing value. */
- /* We must make sure the existing value wasn't created for some other purpose, i.e. */
- /* it can't be an immediate. */
-
- theValue = (TOCValuePtr)cmGetListTail(&theValueHdr->valueList); /* use tail of list */
- if (theValue->flags & kCMImmediate) { /* don't append to this! */
- Container_Disable(container);
- ERROR1(CM_err_BadDefineType, CONTAINERNAME);
- return;
- }
-
- /* If the new definition is such that we can effectively do a concat, we do that */
- /* instead of creating an additional value segment. */
-
- if (theValue->value.notImm.value + theValue->value.notImm.valueLen == offset) {
- theValue->value.notImm.valueLen += size; /* update total size */
- theValueHdr->size += size; /* keep size in valueHdr in sync */
- return;
- }
-
- #if CMKEEP_CONTINUE_FLAG
- /* Have a noncontiguous definition. So here we must create an additional continued */
- /* value segment. */
-
- theValue->flags |= kCMContinued; /* flag the current value as cont'd */
- theValueHdr->valueFlags |= ValueContinued; /* also set a more convenient flag */
- #endif
-
- (void)cmSetValueBytes(container, &valueBytes, Value_NotImm, offset, size);
- cmAppendValue(theValueHdr, &valueBytes, 0);
- }
-
-
- /*----------------------------------------------*
- | CMInsertValueData - insert data into a value |
- *----------------------------------------------*
-
- The buffer is inserted into the value's data at the specified offset. size bytes are
- inserted.
-
- If the current size of the value data is T (it will be 0 for a new value created by
- CMNewValue()), then the offset may be any value from 0 to T. That is, the insertion
- may be anywhere within the data value or the value extended with new data. The value of
- T can be gotton using CMGetValueSize(). Note, no "holes" can be created. An offset
- cannot be greater than T. Also, note, that an insertion at offset T is identical to
- a CMWriteValueData() to the same place.
-
- Once data has been written to the container, it may be read using CMReadValueData(). Note,
- that CMReadValueData() is also used for containers opened with for input using
- CMOpenContainer(). It thus can be used for all kinds of opens. The converse is not true.
- CMWriteValueData() may only be used for a container opend for writing (or converting)
- using CMOpenNewContainer().
-
- It is an error to write to protected values. This includes the predefined TOC objects
- (seed and offset values) and objects representing currently opened embedded containers.
-
- For creating embedded containers, CMOPenNewContainer() is used instead of
- CMWriteValueData(). See CMNewValue() and CMOpenContainer() for further details.
-
- From an implementation point of view, continued (i.e., segmented) values are handled by
- spliting them into one or two new segments. The insertion is always a new segment. The
- original segment to be inserted to will split into two segments if the insertion is in
- to its middle. It will remain intact and unsplit if we are inserting into its beginning.
- The new segment is simply inserted in front of it. For the split case the new segment
- must be inserted between the split pieces.
- */
-
- void CM_FIXEDARGS CMInsertValueData(CMValue value, CMPtr buffer, CMCount offset, CMSize size)
- {
- TOCValueHdrPtr theValueHdr;
- TOCValuePtr theValue, insBeforeValue;
- ContainerPtr container;
- CM_UCHAR *p, *q, *ins;
- CM_ULONG valueSize, insSize, segOffset, insOffset, offset1, actualSize;
- char offsetStr[15];
-
- ExitIfBadValue(value, CM_NOVALUE); /* validate value */
- if (size == 0 || buffer == NULL) return;
-
- theValueHdr = (TOCValueHdrPtr)value;
- container = theValueHdr->container->updatingContainer;
-
- if ((container->useFlags & kCMWriting) == 0) { /* make sure opened for writing */
- Container_Disable(container);
- ERROR1(CM_err_WriteIllegal1, CONTAINERNAME);
- return;
- }
-
- if ((theValueHdr->valueFlags & ValueProtected) != 0){ /* can't write if protected! */
- Container_Disable(container);
- ERROR1(CM_err_WriteIllegal2, CONTAINERNAME);
- return;
- }
-
- if ((theValueHdr->valueFlags & ValueGlobal) != 0) { /* can't write to global names */
- Container_Disable(container);
- ERROR1(CM_err_CantWriteGlbl, CONTAINERNAME);
- return;
- }
-
- if (IsDynamicValue(value)) { /* process dynamic value... */
- GetDynHandlerAddress(value, cmInsertValueData, CMInsertValueDataOpType, "CMInsertValueData", CM_NOVALUE);
- if (IsDynamicValue(value)) {
- SignalDynHandlerInUse(value, cmInsertValueData);
- AllowCMGetBaseValue(container);
- CMDynInsertValueData(value, buffer, offset, size);
- DisAllowCMGetBaseValue(container);
- SignalDynHandlerAvailable(value, cmInsertValueData);
- return;
- }
- theValueHdr = (TOCValueHdrPtr)value; /* ("this") value may have changed! */
- }
-
- /* If the value list is empty, create the first or only value for this value header. */
- /* An immediate value is created if the value size is less than or equal to the */
- /* sizeof(CM_ULONG). Otherwise we insert (and write) the data to the container and set*/
- /* the TOC info with the offset to it. An insert into an empty value list is exactly */
- /* the same as writing to it the first time. So to save some code space a call is */
- /* made to CMWriteValueData() to do the actual work. */
-
- if (cmIsEmptyList(&theValueHdr->valueList)) { /* if we have a empty value list... */
- CMWriteValueData(value, buffer, offset, size); /* write insert (offset must be 0) */
- return;
- } /* end of 1st value */
-
- /* At this point we have EXISTING data (possibly already continued). If the offset */
- /* says that we are NOT writing to the end of the data, then we MUST be inserting the */
- /* new data. If we are writing to the end, then the insert is exactly like a write. */
- /* In that case CMWriteValueData() is called to do the actual work. */
-
- valueSize = CMGetValueSize((CMValue)theValueHdr); /* get current size of value */
-
- if (valueSize == offset) { /* inserting to end of current value*/
- CMWriteValueData(value, buffer, offset, size); /* append insert to end of data */
- return;
- } /* end of appending */
-
- /* Now we know we're actually doing an insert. There are three cases: */
-
- /* (1). Inserting into an immediate. If the size of the immediate plus the size of */
- /* the insert still fits in a immediate the result will remain an immediate. */
- /* Otherwise, the immediate is converted to a non-immediate and we have cases */
- /* (2) or (3). */
- /* (2). Inserting into the start of an existing segment (segment offset 0). The */
- /* insert is made a segment and placed before the existing segment. */
- /* (3). Inserting into the middle of a non-immediate value segment. The existing */
- /* segment must be split into two segments and the insert segment placed between*/
- /* them. */
-
- /* First case (1) -- immediates... */
-
- theValue = (TOCValuePtr)cmGetListTail(&theValueHdr->valueList); /* use tail of list */
-
- if (theValue->flags & kCMImmediate) { /* if current value immediate... */
- if (valueSize + size <= sizeof(CM_ULONG)) { /* insert new data if we can... */
- insSize = (CM_ULONG)size;
- p = theValue->value.imm.ucharsValue + valueSize;
- q = p + size;
- ins = theValue->value.imm.ucharsValue + offset;
- while (--p >= ins) *--q = *p;
- q = (CM_UCHAR *)buffer;
- while (size--) *++p = *q++;
- theValue->value.notImm.valueLen = valueSize + insSize;
- theValueHdr->size += insSize;
- cmTouchImmediateValue(theValueHdr); /* touch for updating if necessary */
- return; /* that's all we need to do! */
- }
-
- if (!cmConvertImmediate(theValue)) /* convert the immediate... */
- return; /* (exit if convert failure) */
- } /* end of immediate */
-
- /* At this point we have either cases (2) or (3). Get the offset within the segment */
- /* and the segment itself to determine which case we got. */
-
- insBeforeValue = cmGetStartingValue(theValueHdr, offset, &segOffset);
- if (insBeforeValue == NULL) { /* offset MUST be IN the value */
- Container_Disable(container);
- ERROR2(CM_err_Offset2Big, cmltostr(offset, 1, false, offsetStr), CONTAINERNAME);
- return;
- }
-
- /* Write the new inserted data to the container... */
-
- insOffset = cmGetFreeListEntry(container, (CM_ULONG)size, true, &actualSize);
- if (actualSize != 0) /* if we got some to reuse... */
- CMfseek(container, insOffset, kCMSeekSet); /* ...position to write over it */
- else { /* if couldn't find a fit... */
- insOffset = CMgetContainerSize(container); /* write insert to end of container */
- CMfseek(container, 0, kCMSeekEnd);
- }
-
- if (CMfwrite(container, buffer, sizeof(CM_UCHAR), size) != size) {
- Container_Disable(container);
- ERROR1(CM_err_BadWrite, CONTAINERNAME);
- return;
- }
-
- offset1 = insOffset + size; /* update logical OR physical EOF */
- if (actualSize == 0)
- container->physicalEOF = offset1; /* update next free container byte */
- SetLogicalEOF(offset1); /* set logical EOF (may != physical)*/
-
- /* Create the new value segment to point to the inserted data and insert the segment */
- /* into the appropriate place. For case (2) segOffset will be 0 and for (3) non-zero.*/
- /* The actual segment create and insert is done by a separate routine. This is due to */
- /* possible updating which uses the same routine. See comments in cmInsertNewSegment()*/
- /* (in ValueRtn.c ). */
-
- cmInsertNewSegment(theValueHdr, insBeforeValue, segOffset, insOffset, size);
-
- /* If we're recording updates, then define touched list entry for the insert... */
-
- cmTouchEditedValue(theValueHdr); /* touch for updating if necessary */
- }
-
-
- /*----------------------------------------------*
- | CMDeleteValueData - delete data from a value |
- *----------------------------------------------*
-
- Deletes size bytes from the value data starting at the offset. If the offset is greater
- than the value data size, nothing is deleted. The amount to delete may be greater than
- the current data size. In that case, all the data starting from the offset will be
- deleted. If ALL the data is deleted, the value is defined as null, i.e. a data length of
- 0.
- */
-
- void CM_FIXEDARGS CMDeleteValueData(CMValue value, CMCount offset, CMSize size)
- {
- TOCValueHdrPtr theValueHdr;
- TOCValuePtr theValue;
- ContainerPtr container;
- CM_UCHAR *p, *q;
- CM_ULONG valueSize, startOffset, endOffset;
-
- ExitIfBadValue(value, CM_NOVALUE); /* validate value */
- if (size == 0) return;
-
- theValueHdr = (TOCValueHdrPtr)value;
- container = theValueHdr->container->updatingContainer;
-
- if ((container->useFlags & kCMWriting) == 0) { /* make sure opened for writing */
- Container_Disable(container);
- ERROR1(CM_err_WriteIllegal1, CONTAINERNAME);
- return;
- }
-
- if ((theValueHdr->valueFlags & ValueProtected) != 0){ /* can't write if protected! */
- Container_Disable(container);
- ERROR1(CM_err_WriteIllegal2, CONTAINERNAME);
- return;
- }
-
- if ((theValueHdr->valueFlags & ValueGlobal) != 0) { /* can't write to global names */
- Container_Disable(container);
- ERROR1(CM_err_CantWriteGlbl, CONTAINERNAME);
- return;
- }
-
- if (IsDynamicValue(value)) { /* process dynamic value... */
- GetDynHandlerAddress(value, cmDeleteValueData, CMDeleteValueDataOpType, "CMDeleteValueData", CM_NOVALUE);
- if (IsDynamicValue(value)) {
- SignalDynHandlerInUse(value, cmDeleteValueData);
- AllowCMGetBaseValue(container);
- CMDynDeleteValueData(value, offset, size);
- DisAllowCMGetBaseValue(container);
- SignalDynHandlerAvailable(value, cmDeleteValueData);
- return;
- }
- theValueHdr = (TOCValueHdrPtr)value; /* ("this") value may have changed! */
- }
-
- /* If the offset specifies that the end of the value is to be deleted, do nothing. We */
- /* let this through since if the user doesn't want any data there s/he's got exactly */
- /* what s/he thinks, i.e., no data there! There never was, but who cares. Note, that */
- /* by asking this question we also know whether any data exists for the value. If the */
- /* offset is less than the size, then the size must be non-zero and hence we have some*/
- /* deleatable data. */
-
- valueSize = CMGetValueSize((CMValue)theValueHdr); /* get current size of value */
- if (offset >= valueSize) return; /* if deleting from end, just exit */
-
- /* Ok, we got to do some work and delete something. Let's get the starting */
- /* and ending segments and the offsets within them for starters. [If you haven't */
- /* guessed, I am making this algorithm up as I go along -- this should be interesting]*/
-
- startOffset = offset; /* initially get data offsets... */
- endOffset = startOffset + size - 1; /* end offset limited to end of data*/
- if (endOffset >= valueSize)
- endOffset = valueSize - 1;
-
- /* Immediate values are handled separately... */
-
- theValue = (TOCValuePtr)cmGetListTail(&theValueHdr->valueList);
- if (theValue->flags & kCMImmediate) { /* if immediate... */
- p = theValue->value.imm.ucharsValue; /* ...do a simple byte delete */
- q = p + startOffset;
- p += endOffset + 1;
- theValueHdr->size -= endOffset - startOffset + 1;
- theValue->value.notImm.valueLen = theValueHdr->size;
- while (++endOffset < valueSize) *q++ = *p++;
- cmTouchImmediateValue(theValueHdr); /* touch for updating if necessary */
- return; /* that's it for immediates */
- }
-
- /* Delete the data from the segments. The actual deletion is done by a separate */
- /* routine. This is due to possible updating which wants to use the same routine. */
- /* See comments in cmDeleteSegmentData() (in ValueRtn.c ). */
-
- cmDeleteSegmentData(theValueHdr, startOffset, endOffset);
-
- /* If we're recording updates, then define touched list entry for the data delete... */
-
- cmTouchEditedValue(theValueHdr); /* touch for updating if necessary */
- }
-
-
- /*-------------------------------------------------------*
- | CMMoveValue - move a value from one object to another |
- *-------------------------------------------------------*
-
- Moves the specified value from its original object property to the specified object
- property. The value is physically deleted from its original object/property as if a
- CMDeleteValue() were done on it. If the value deleted is the only one for the property,
- the property itself is deleted as in CMDeleteObjectProperty().
-
- The value is added to the "to"s object propery in a manner similar to a CMNewValue().
- The order of the values for both the value's original object property and for the value's
- new object property may be changed.
-
- Note, that although the effect of a move is a combination CMDeleteValue()/CMNewValue(),
- THE INPUT REFNUM REMAINS VALID! Its association is now with the new object property.
-
- This operation may be done at any time. No data need be assoicated with the value at the
- time of the move. Only moves WITHIN THE SAME CONTAINER are allowed.
-
- */
-
- void CM_FIXEDARGS CMMoveValue(CMValue value, CMObject object, CMProperty property)
- {
- TOCValueHdrPtr theFromValueHdr, theRealValueHdr, botLayerValueHdr;
- ContainerPtr container;
- CMObjectID typeID;
-
- ExitIfBadValue(value, CM_NOVALUE); /* validate value */
-
- theFromValueHdr = (TOCValueHdrPtr)value;
- container = theFromValueHdr->container;
-
- ExitIfBadObject(object, CM_NOVALUE); /* validate object */
- ExitIfBadProperty(property, CM_NOVALUE); /* validate property */
-
- { ContainerPtr objCtr = ((TOCObjectPtr)object)->container;
- ContainerPtr propCtr = ((TOCObjectPtr)property)->container;
-
- if (container->targetContainer != objCtr->targetContainer ||
- container->targetContainer != propCtr->targetContainer) {
- Container_Disable(container);
- Container_Disable(objCtr);
- Container_Disable(propCtr);
- ERROR3(CM_err_3Containers, CONTAINERNAMEx(container),
- CONTAINERNAMEx(objCtr),
- CONTAINERNAMEx(propCtr));
- return;
- }
- }
-
- if ((container->updatingContainer->useFlags & kCMWriting) == 0) {
- Container_Disable(container);
- ERROR1(CM_err_MoveIllegal, CONTAINERNAME);
- return;
- }
-
- /* If the value header is a dynamic value then we must move both the dynamic value */
- /* and its base ("real") value. These are linked with the base value pointing to the */
- /* its dynamic value. For layered dynamic values, each dynamic value is back linked */
- /* to its base with the bottom layer having the "real" value as its base. Only the */
- /* bottom layer is actually on the special property chain for dynamic values */
- /* associated with its "real value object. Only the top layer should ever be passed. */
- /* We can check for this. We also get the bottom layer and "real" value header */
- /* pointers. You will see how we use these after we get them and do the validation. */
-
- if (IsDynamicValue(theFromValueHdr)) { /* if we have dynamic value... */
- theRealValueHdr = theFromValueHdr; /* start with current value */
- do { /* search for base value... */
- if ((theRealValueHdr->valueFlags & ValueOffPropChain) == 0)
- botLayerValueHdr = theRealValueHdr; /* remember where bot. layer is */
- theRealValueHdr = DYNEXTENSIONS(theRealValueHdr)->baseValue;
- } while (IsDynamicValue(theRealValueHdr)); /* loop till we find real value */
-
- if (theRealValueHdr->dynValueData.dynValue == NULL || /* do the validation... */
- theRealValueHdr->dynValueData.dynValue != theFromValueHdr) {
- Container_Disable(container);
- ERROR2(CM_err_BadRealValue, "CMMoveValue", CONTAINERNAME); /* ...oops! */
- return;
- }
-
- /* We now have the bottom (or only) layer value header and its base ("real") value. */
- /* We can move the bottom layer to the destination by simply recursively calling */
- /* ourselves. The bottom layer is, from a data structure point of view, a value */
- /* header on a property chain. All the higher layers sort of float and will still */
- /* be pointing to the moved value. In order though to pull this "stunt", we must */
- /* temporarily pretend that the dynamic value isn't a dynamic value. That way we */
- /* won't get back in this dynamic value code and will simply move it with the code */
- /* outside this if block. Note the object we use here is the destination object, */
- /* but the property is the one corresponding to dynamic values. */
-
- botLayerValueHdr->valueFlags &= ~ValueDynamic; /* pretend it's not dynamic */
- typeID = botLayerValueHdr->typeID; /* hold on to the type ID... */
- botLayerValueHdr->typeID = 0; /* stop potential dup types errs*/
- CMMoveValue((CMValue)botLayerValueHdr,object,container->dynValueProperty);/* move it*/
- botLayerValueHdr->typeID = typeID; /* put back type ID */
- botLayerValueHdr->valueFlags |= ValueDynamic; /* put back the flag */
-
- /* Now it's the "real" base value's turn to be moved. It does not need further */
- /* validating. So all we need to do is "fall" through to the normal value case */
- /* below. It will move to the destination and be placed on the desired property */
- /* chain. The dynamic value (layer) is there "waiting" for it. Since all the */
- /* refNums remain the same and so do the fields (except for the property links of */
- /* course), everything is properly linked. */
-
- theFromValueHdr = theRealValueHdr; /* pretend base was passed */
- } /* dynamic value */
-
- /* If we're recording updates, then define touched list entry for the move. This MUST */
- /* be done before the move so that the touch list entry can be generated in terms of */
- /* the original object, property, and type. The move can change them. */
-
- cmTouchMovedValue(theFromValueHdr, theFromValueHdr->theProperty->theObject,
- (TOCObjectPtr)object, ((TOCObjectPtr)property)->objectID);
-
- /* Move the value (header). The actual move is done by a separate routine. This is */
- /* due to possible updating which wants to use the same routine. See comments in */
- /* cmMoveValueHdr() (in ValueRtn.c ). */
-
- cmMoveValueHdr(theFromValueHdr, object, property);
- }
-
-
- /*--------------------------------------------*
- | CMGetValueInfo - return info about a value |
- *--------------------------------------------*
-
- The specified information for the refNum associated with a value is returned. A parameter
- may be NULL indicating that info is not needed.
- */
-
- void CM_FIXEDARGS CMGetValueInfo(CMValue value, CMContainer CM_PTR *container0,
- CMObject CM_PTR *object, CMProperty CM_PTR *property,
- CMType CM_PTR *type, CMGeneration CM_PTR *generation)
- {
- TOCValueHdrPtr theValueHdr;
- ContainerPtr container; /* this is to make error macro work */
-
- ExitIfBadValue(value, CM_NOVALUE); /* validate value */
-
- container = ((TOCValueHdrPtr)value)->container;
-
- if (IsDynamicValue(value)) { /* process dynamic value... */
- GetDynHandlerAddress(value, cmGetValueInfo, CMGetValueInfoOpType, "CMGetValueInfo", CM_NOVALUE);
- if (IsDynamicValue(value)) {
- SignalDynHandlerInUse(value, cmGetValueInfo);
- AllowCMGetBaseValue(container);
- CMDynGetValueInfo(value, container0, object, property, type, generation);
- DisAllowCMGetBaseValue(container);
- SignalDynHandlerAvailable(value, cmGetValueInfo);
- return;
- }
- }
-
- theValueHdr = (TOCValueHdrPtr)value; /* ("this") value may have changed! */
-
- if (container0) /* container... */
- *container0 = (CMContainer)container;
-
- if (object) /* object... */
- *object = (CMObject)theValueHdr->theProperty->theObject;
-
- if (property) /* property... */
- *property = (CMProperty)cmFindObject(container->toc, theValueHdr->theProperty->propertyID);
-
- if (type) /* type... */
- *type = (CMType)cmFindObject(container->toc, theValueHdr->typeID);
-
- if (generation) /* generation... */
- *generation = theValueHdr->generation;
- }
-
-
- /*------------------------------------------*
- | CMSetValueType - set the type of a value |
- *------------------------------------------*
-
- The type ID from the type is set for the specified value.
-
- What do you know? A short comment!
- */
-
- void CM_FIXEDARGS CMSetValueType(CMValue value, CMType type)
- {
- TOCValueHdrPtr theValueHdr, theValueHdr0;
- ContainerPtr container;
- CM_ULONG typeID;
-
- ExitIfBadValue(value, CM_NOVALUE); /* validate value */
- ExitIfBadType(type, CM_NOVALUE); /* validate type */
-
- container = ((TOCValueHdrPtr)value)->container;
-
- { ContainerPtr typeCtr = ((TOCObjectPtr)type)->container;
-
- if (container->targetContainer != typeCtr->targetContainer) {
- Container_Disable(container);
- Container_Disable(typeCtr);
- ERROR2(CM_err_2Containers, CONTAINERNAMEx(container),
- CONTAINERNAMEx(typeCtr));
- return;
- }
- }
-
- if (IsDynamicValue(value)) { /* process dynamic value... */
- GetDynHandlerAddress(value, cmSetValueType, CMSetValueTypeOpType, "CMSetValueType", CM_NOVALUE);
- if (IsDynamicValue(value)) {
- SignalDynHandlerInUse(value, cmSetValueType);
- AllowCMGetBaseValue(container);
- CMDynSetValueType(value, type);
- DisAllowCMGetBaseValue(container);
- SignalDynHandlerAvailable(value, cmSetValueType);
- return;
- }
- }
-
- theValueHdr = (TOCValueHdrPtr)value; /* ("this") value may have changed! */
-
- /* Make sure the type doesn't already exist for theobject's property... */
-
- typeID = ((TOCObjectPtr)type)->objectID;
-
- theValueHdr0 = cmGetPropertyType(theValueHdr->theProperty, typeID);
- if (theValueHdr0 != NULL && theValueHdr != theValueHdr0) {
- Container_Disable(container);
- ERROR2(CM_err_DupType, cmGetGlobalTypeName(container, typeID), CONTAINERNAME);
- return;
- }
-
- /* If we're recording updates, then define touched list entry for the set-info. This */
- /* touch MUST be done before any changes to the type because the touch list info is */
- /* in terms of the original type. */
-
- cmTouchSetInfoedValue(theValueHdr); /* touch the value if necessary */
-
- /* Change the type... */
-
- theValueHdr->typeID = typeID; /* there goes the type */
- }
-
-
- /*-------------------------------------------------------------*
- | CMSetValueGeneration - set the generation number of a value |
- *-------------------------------------------------------------*
-
- The generation for the specified value is set. The generation number must be greater than
- or equal to 1.
-
- Normally this routine doesn't need to be used since the generation is set when a value is
- created. The default generation number is that of the container.
- */
-
- void CM_FIXEDARGS CMSetValueGeneration(CMValue value, CMGeneration generation)
- {
- TOCValueHdrPtr theValueHdr;
- ContainerPtr container;
- char genStr[15];
-
- ExitIfBadValue(value, CM_NOVALUE); /* validate value */
-
- container = ((TOCValueHdrPtr)value)->container;
-
- if (IsDynamicValue(value)) { /* process dynamic value... */
- GetDynHandlerAddress(value, cmSetValueGen, CMSetValueGenOpType, "CMSetValueGeneration", CM_NOVALUE);
- if (IsDynamicValue(value)) {
- SignalDynHandlerInUse(value, cmSetValueGen);
- AllowCMGetBaseValue(container);
- CMDynSetValueGen(value, generation);
- DisAllowCMGetBaseValue(container);
- SignalDynHandlerAvailable(value, cmSetValueGen);
- return;
- }
- }
-
- theValueHdr = (TOCValueHdrPtr)value; /* ("this") value may have changed! */
-
- /* If we're recording updates, then define touched list entry for the set-info. This */
- /* touch MUST be done before any changes to the type because the touch list info is */
- /* in terms of the original type. */
-
- cmTouchSetInfoedValue(theValueHdr); /* touch the value if necessary */
-
- /* Change generation... */
-
- if (generation < 1) { /* validate the generation nbr */
- Container_Disable(container);
- ERROR3(CM_err_BadGenNbr, cmltostr(generation, 1, false, genStr), "CMSetValueGeneration", CONTAINERNAME);
- return;
- }
-
- theValueHdr->generation = generation; /* finally, set the generation nbr */
- }
-
-
- /*----------------------------------------------------------------------*
- | CMGetValueContainer - get the container refNum for one of its values |
- *----------------------------------------------------------------------*
-
- The refNum for the container which contains the specified value is returned.
-
- Note that CMGetValueInfo() also returns the container refNum. But this is generally
- useful enough on its own (especially in handlers) to warrent a separate API call.
- Further, it provides a symmetry with CMGetObjectContainer().
- */
-
- CMContainer CM_FIXEDARGS CMGetValueContainer(CMValue value)
- {
- ExitIfBadValue((TOCValueHdrPtr)value, NULL); /* validate value */
-
- return ((CMContainer)((TOCValueHdrPtr)value)->container); /* return container */
- }
-
-
- /*-------------------------------------------------*
- | CMGetValueRefCon - return user's value "refCon" |
- *-------------------------------------------------*
-
- This routine returns the user's "refCon" (reference constant) that s/he may associate
- with any value refNum (i.e., a CMValue). The refCon is a CM_ULONG that the user may use
- in any way. It is not touched by the API except to init it to 0 when the value is
- initially created.
-
- Note, the refCon is NOT perserved across closed containers, i.e., it is not saved in the
- TOC.
- */
-
- CMRefCon CM_FIXEDARGS CMGetValueRefCon(CMValue value)
- {
- ExitIfBadValue((TOCValueHdrPtr)value, (CMRefCon)0); /* validate value */
-
- return (((TOCValueHdrPtr)value)->valueRefCon); /* return user's refCon */
- }
-
-
- /*--------------------------------------------------*
- | CMSetValueRefCon - set the user's value "refCon" |
- *--------------------------------------------------*
-
- This routine is used to set the user's "refCon" (reference constant) to be assoicated with
- an value. The refCon is a CM_ULONG that the user may use in any way. It is not touched by
- the API.
-
- Note, the refCon is NOT perserved across closed containers, i.e., it is not saved in the
- TOC.
- */
-
- void CM_FIXEDARGS CMSetValueRefCon(CMValue value, CMRefCon refCon)
- {
- ExitIfBadValue((TOCValueHdrPtr)value, CM_NOVALUE); /* validate value */
-
- ((TOCValueHdrPtr)value)->valueRefCon = refCon; /* set the user's refCon */
- }
-
-
- /*--------------------------------*
- | CMDeleteValue - delete a value |
- *--------------------------------*
-
- The value is deleted from its object property. If the value deleted is the only one for
- the property, the property itself is deleted as in CMDeleteObjectProperty().
-
- Calling CMDeleteValue() produces an implicit call on CMReleaseValue(). Hence the passed
- value refNum's association to the value is destroyed. There should be no further
- operations on the refNum once CMDeleteValue() is called.
-
- Note, some values are protected from deletion. This includes the predefined TOC object
- values (seed and offset) and the currently open embedded container values. Also, a value
- acting as a base value for a subvalue cannot be deleted.
- */
-
- void CM_FIXEDARGS CMDeleteValue(CMValue value)
- {
- TOCValueHdrPtr theValueHdr, theRealValueHdr;
- ContainerPtr container;
-
- ExitIfBadValue(value, CM_NOVALUE); /* validate value */
-
- theValueHdr = (TOCValueHdrPtr)value;
- container = theValueHdr->container->updatingContainer;
-
- if ((container->useFlags & kCMWriting) == 0) {
- Container_Disable(container);
- ERROR2(CM_err_DeleteIllegal, "value", CONTAINERNAME);
- return;
- }
-
- if ((theValueHdr->valueFlags & (ValueProtected | ValueUndeletable)) != 0) {
- Container_Disable(container);
- ERROR1(CM_err_CantDelete3, CONTAINERNAME);
- return;
- }
-
- if (theValueHdr->useCount > 1) { /* there are other users! */
- Container_Disable(container);
- ERROR1(CM_err_CantDelete4, CONTAINERNAME); /* nice try though! */
- return;
- }
-
- /* If the value header is a dynamic value then we must delete both the dynamic value */
- /* and its base ("real") value. These are linked with the base value pointing to the */
- /* its dynamic value. For layered dynamic values, each dynamic value is back linked */
- /* to its base with the bottom layer having the "real" value as its base. Only the */
- /* bottom layer is actually on the special property chain for dynamic values */
- /* associated with its "real value object. Only the top layer should ever be passed. */
- /* We can check for this. We also remember the "real" value header pointer. You will */
- /* see how we use this after we get it and do the validation. */
-
- if (IsDynamicValue(theValueHdr)) { /* if we have dynamic value... */
- theRealValueHdr = theValueHdr; /* start with current value */
- do /* search for real value... */
- theRealValueHdr = DYNEXTENSIONS(theRealValueHdr)->baseValue;
- while (IsDynamicValue(theRealValueHdr)); /* loop till we find base value */
-
- if (theRealValueHdr->dynValueData.dynValue == NULL || /* do the validation... */
- theRealValueHdr->dynValueData.dynValue != theValueHdr) {
- Container_Disable(container);
- ERROR2(CM_err_BadRealValue, "CMDeleteValue", CONTAINERNAME); /* ...oops! */
- return;
- }
-
- /* We now got our hands on the base ("real") value and we're happy with it. By doing*/
- /* a CMReleaseValue() on the dynamic value we will cause its deletion from there. */
- /* It is expected that if the dynamic value is layered, the handlers will */
- /* recursively call CMReleaseValue() on each of their base values down to the bottom */
- /* layer. So by the time we return to here all the dynamic values should be gone */
- /* for its base value we just found above. So then all we have to do is delete the */
- /* base ("real") value. */
-
- CMReleaseValue(value); /* this should kill dynamics */
- theValueHdr = theRealValueHdr; /* now set to kill the base */
- }
-
- /* If we're recording updates, then define touched list entry for the value delete... */
-
- cmTouchDeletedValue(theValueHdr, theValueHdr->theProperty->theObject);
-
- /* Deleted value headers are placed on a chain and marked deleted. The value segments */
- /* are freed. Thus any attempted reuse of the refNum will be caught. Although we NULL*/
- /* out the caller's refNum s/he could have one squirreled away some where. */
-
- cmMarkValueDeleted(container, theValueHdr, false);
- }
-
-
- /*----------------------------------------------------------------------*
- | CMReleaseValue - destroy association between an value and its refNum |
- *----------------------------------------------------------------------*
-
- The association between the refNum and the value is destroyed. There should be no further
- operations on the refNum once this routine is called.
-
- If the value is a dynamic value, then if there are no other users of the value (i.e.,
- its use count is 1), the "release" dynamic value value handler is called for the value
- and ALL of its lower layer dynamic values. It here are other uses of the dynamic value
- (i.e., its use count is >1), we do nothing other than decrement the use count by 1.
-
- See comments in code below (you can't miss 'em) on how and why we delete all the dynamic
- value layers. The release handlers are not expected to release their base value like
- other value handler operations.
- */
-
- void CM_FIXEDARGS CMReleaseValue(CMValue value)
- {
- TOCValueHdrPtr theBaseValueHdr, dynamicBaseValueHdr, theValueHdr = (TOCValueHdrPtr)value;
- ContainerPtr container;
- DynValueHdrExtPtr extensions;
- CMHandlerAddr handler;
-
- ExitIfBadValue(value, CM_NOVALUE); /* validate value */
-
- container = theValueHdr->container;
-
- /* As discussed later, dynamic value release handlers must not release their base */
- /* value because we do it all here. To protect agains such "mistakes" we check for */
- /* the attempt. The dynamicBaseValueHdr in the container is normally NULL. If it */
- /* is not NULL it was set when we called a release handler (later) to that handler's */
- /* base value. If it attempts a CMReleaseValue(CMGetBaseValue(value)), we will */
- /* intercept it here and yell. Note, we only test for the base value of the handler. */
- /* This lets other releases, which a release handler might want to do, get through. */
- /* Since one of those releases could be for yet another dynamic value, we have to */
- /* protect the container's version of dynamicBaseValueHdr. We can do this as a local */
- /* variable since a call back to this routine from a release handler is recursive and */
- /* won't hurt the up-level copy. */
-
- dynamicBaseValueHdr = container->dynamicBaseValueHdr;/* save current "switch" setting */
- if (theValueHdr == dynamicBaseValueHdr) { /* if attempted release on base... */
- Container_Disable(container);
- ERROR1(CM_err_BaseRelAttempted, CONTAINERNAME); /* ...yell */
- return;
- }
-
- /* Releasing a value that already is released is an error... */
-
- if (theValueHdr->useCount == 0) {
- Container_Disable(container);
- ERROR1(CM_err_AlreadyReleased1, CONTAINERNAME);
- return;
- }
-
- /* Decrement the use count... */
-
- --theValueHdr->useCount; /* decrement use count */
-
- /* If this is not a dynamic value, just exit. With dynamic values there's just a */
- /* "little" bit more to do! */
-
- if (!IsDynamicValue(value)) return;
-
- /* We have a dynamic value at this point. If its use count has gone to 0, then it */
- /* is time to call the release handler for this value and ALL of its lower layers. */
- /* When each handler returns we can delete the value. */
-
- if (theValueHdr->useCount > 0) return; /* dynamic value still in use */
-
- /* The following code calls the release handler for each dynamic value in a layered */
- /* set. Each layer is back-linked to its base with the bottom layer having the "real" */
- /* value as its base. Only the bottom layer is actually on the special property chain */
- /* for dynamic values associated with its "real" value object. Only the top layer */
- /* should ever be passed. */
-
- /* The calling of the dynamic value handler is different here from the way all other */
- /* dynamic handlers are called for value operations. Here WE CALL EACH DYNAMIC VALUE */
- /* HANDLER FOR EACH LAYER EXPLICITLY. A release dynamic handler does NOT do a release */
- /* on its base value. The reason is that a single "use" (or "new") was done to create */
- /* a dynamic value. The CMReleaseValue() to get here is its counterpart. The user */
- /* has no knowledge (sort of) that the type may have had base types that spawned */
- /* lower dynamic value layers. */
-
- /* The other reason we do all the releases from here is for safety. We cannot "trust" */
- /* the handlers to do the release of their base value. That would leave the data */
- /* structures in a weird state. By processing all the layers ourselves we can be sure*/
- /* each release handler is called and each dynamic value is properly freed. */
-
- /* Finally, the following loop does the releases following each dynamic value's back */
- /* line. That means we're releasing in the reverse order of creation. That seems the*/
- /* proper order to do it. If the handlers released their base values, the effect */
- /* would be the reverse. */
-
- do { /* loop up through all the layers...*/
- extensions = DYNEXTENSIONS(theValueHdr); /* save a little code generation */
- theBaseValueHdr = extensions->baseValue; /* we will be deleting theValueHdr */
-
- /* We must now explicitly do the basic guts of a cmGetDynHandlerAddress(). That */
- /* routine can't be used because it will go after an inherited release handler if */
- /* there is not handler for the value. We don't want that because we want to call */
- /* each release handler explicitly from here -- only if its got one of course. */
-
- /* We know this is the first and only time a full CMReleaseValue() is being done for*/
- /* this value. We don't get into here unless the use count goes to 0. So all we */
- /* have to do to see if there is a release handler is to call the value operations */
- /* metahandler for this value. That will tell us if there is a release handler for */
- /* this value or it wants to inherit one. If there is a release handler, we call it */
- /* in the normal way so that this layer can do any cleanup (e.g., close files, free */
- /* refCon memory, etc.). If there is none, everyone is happy (at this layer at */
- /* least). In either case we merge into the delete code that follows it. */
-
- handler = (CMHandlerAddr)(*extensions->metaHandler)(NULL, (CMGlobalName)CMReleaseValueOpType);
-
- /* If we got a non-inherited release handler for this value, call it. By copying */
- /* this value's base value into the container value, dynamicBaseValueHdr, we can */
- /* test on entry to this routine for an attempted release on the base value. We */
- /* said above that the rules are that the handler are NOT to do that. */
-
- if (handler) { /* call release handler if we got it*/
- SignalDynHandlerInUse(theValueHdr, cmReleaseValue);
- AllowCMGetBaseValue(container);
- container->dynamicBaseValueHdr = theBaseValueHdr;
- extensions->dynValueVector.cmReleaseValue.handler = handler;/* set for the macro */
- CMDynReleaseValue(theValueHdr);
- container->dynamicBaseValueHdr = dynamicBaseValueHdr;
- DisAllowCMGetBaseValue(container);
- SignalDynHandlerAvailable(theValueHdr, cmReleaseValue);
- }
-
- /* Now we must mark the value as deleted. This means moving it to the deletedValues */
- /* list. For the bottom layer we can mark the value in the normal way and remove */
- /* the pointer in the "real" value to it. For the upper layers we must explicitly */
- /* move the value to the deletedValues list and mark it. We can tell which is which */
- /* because cmNewDynamicValue() flagged the upper layers (that was nice of it -- */
- /* well, this is why it did it, so it wasn't really being nice). */
-
- /* Note, when the bottom layer is marked as deleted, the entire "dynamic values" */
- /* property will be deleted if this is the last (or only) dynamic value for that */
- /* property. This is a good thing, because we will report an error if we ever see */
- /* the "dynamic values" property at continer close time. */
-
- /* Aslo note that freeing the last dynamic value for an object clears the dynamic */
- /* value object flag, DynamicValuesObject, which is used to protect against users */
- /* deleting the object prematurly. */
-
- if ((theValueHdr->valueFlags & ValueOffPropChain) != 0) { /* if upper layer... */
- if (cmKeepDeletedRefNums(container->toc)) { /* ...keep refNums ? */
- theValueHdr->valueFlags |= ValueDeleted; /* ...mark value deleted*/
- cmInitList(&theValueHdr->valueList); /* ...done for safety */
- cmAppendListCell(&container->deletedValues,theValueHdr);/* ...move to list */
- } else /* if don't keep refNums*/
- CMfree(theValueHdr); /* ...free it */
- } else { /* if bottom layer... */
- TOCPropertyPtr theProperty = theValueHdr->theProperty; /* ...point to dyn prop.*/
- if (cmCountListCells(&theProperty->valueHdrList) == 1) /* ...if last dyn value */
- theProperty->theObject->objectFlags &= ~DynamicValuesObject;/* ...clear obj flag*/
- extensions->baseValue->dynValueData.dynValue = NULL; /* ...fix real value */
- cmMarkValueDeleted(container, theValueHdr, false); /* ...mark value deleted*/
- if (container->targetContainer) /* if target not freed */
- if (container->targetContainer->nbrOfDynValues > 0) /* ...adjust total count*/
- --container->targetContainer->nbrOfDynValues;
- }
-
- CMfree(extensions); /* free the extensions */
-
- theValueHdr = theBaseValueHdr; /* get next layer "down" */
- } while (IsDynamicValue(theValueHdr)); /* loop down to real value */
-
- /* We have now deleted all the dynamic value layers for the spawning "real" value. */
- /* At this point theValueHdr is pointing at that real value. When it spawned the */
- /* dynamic value(s) we set its use count to 1. So we "owe" one decrement here. */
-
- if (theValueHdr->useCount > 0)
- --theValueHdr->useCount; /* decrement real value's use count */
- }
-
- CM_END_CFUNCTIONS
-